/**任务 */
type Task = (next: () => Promise<void>) => void | Promise<void>

/**洋葱模型
 * @tips 这里巧妙的运用了事件队列，调用next的就需要等下一个任务完成。
 * 而在下一个任务期间，可能又调用了next，需要等下下个人，一层层await，最终才回到第一层
 * @example
 * const onion = new Onion()
 * onion.addTask(async (next) => {
 *     console.log('开始');
 *     await next()
 *     console.log('结束');
 * })
 * onion.addTask(() => {
 *     console.log(1);
 * })
 * onion.addTask(async (next) => {
 *     console.log(`2开`);
 *     await next()
 *     console.log(`2关`);
 * })
 * onion.addTask(() => {
 *     console.log(3);
 * })
 * onion.run()// 开始  1  2开 3  2关  结束
 */
class Onion {
    /**任务列表 */
    private taskList: Task[] = []
    /**是否正在运行 */
    private isRunning = false //防止连续调用run函数
    /**当前运行到第几个任务 */
    private nowIndex = 0

    /**添加任务 */
    addTask(task: Task) {
        this.taskList.push(task)
    }
    /**运行所有任务 */
    run() {
        if (this.isRunning || !this.taskList.length) return
        this.isRunning = true
        this.runOneTask()
    }

    /**取出单个任务执行 */
    private async runOneTask() {
        //任务执行完了
        if (this.nowIndex >= this.taskList.length) {
            this.reset()
            return
        }
        /**判断用户是否手动调用next - 获取任务前的序号 */
        const preIndex = this.nowIndex
        const task = this.taskList[this.nowIndex]
        await task(this.next)
        /**判断用户是否手动调用next - 获取任务后的序号 */
        const curIndex = this.nowIndex
        if (preIndex === curIndex) {
            await this.next()
        }

    }
    /**执行下一个任务*/
    private next = async () => { //这里需要使用箭头函数，因为next可能是其它人调用，而不是实例本身
        this.nowIndex++
        await this.runOneTask()
    }
    /**重置 */
    private reset() {
        this.isRunning = false
        this.taskList = []
        this.nowIndex = 0
    }
}

export default Onion